package com.d.lib.ui.view.clock;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

public class ClockSetView extends Component implements Component.EstimateSizeListener,
    Component.DrawTask, Component.TouchEventListener {

  public static final int MODE_HOUR = 0;
  public static final int MODE_MINUTE = 1;

  private final String[] HOURS_AM = new String[]{"12", "1", "2", "3", "4", "5", "6", "7", "8", "9"
      , "10", "11"};
  private final String[] HOURS_PM = new String[]{"00", "13", "14", "15", "16", "17", "18", "19",
      "20", "21", "22", "23"};
  private final String[] MINUTE = new String[]{"00", "05", "10", "15", "20", "25", "30", "35",
      "40", "45", "50", "55"};

  private int mWidth;
  private int mHeight;

  private int mMode = MODE_HOUR;
  private boolean mShift;

  private Paint mPaint;
  // private Xfermode[] mXfermodes;
  private Color mColorBg, mColorMain, mColorSub, mColorFocusMain, mColorFocusSub, mColorIndicator;
  private int mTextMainSize, mTextSubSize;
  private float mRadiusBig, mRadiusSmall;
  private float mIndicatorWidth;
  private float mPadding;
  private float mTextHeight, mTextHeightMain, mTextHeightSub;
  private int mSweepAngle = 90;
  private OnSelectListener mListener;

  public ClockSetView(Context context, AttrSet attrSet, int resId) {
    super(context, attrSet, resId);
    initAttrs(context, attrSet);
    init(context);
  }

  public ClockSetView(Context context, AttrSet attrSet, String styleName) {
    super(context, attrSet, styleName);
    initAttrs(context, attrSet);
    init(context);
  }

  public ClockSetView(Context context, AttrSet attrSet) {
    super(context, attrSet);
    initAttrs(context, attrSet);
    init(context);
  }

  public ClockSetView(Context context) {
    super(context);
    initAttrs(context, null);
    init(context);
  }

  private void initAttrs(Context context, AttrSet attrs) {
    mColorBg = attrs.getAttr("lib_ui_view_clocksetv_background").isPresent() ? attrs.getAttr(
        "lib_ui_view_clocksetv_background").get().getColorValue() : new Color(0xFFFAFAFA);
    mColorMain = attrs.getAttr("lib_ui_view_clocksetv_mainColor").isPresent() ? attrs.getAttr(
        "lib_ui_view_clocksetv_mainColor").get().getColorValue() : new Color(0xFF000000);
    mColorSub = attrs.getAttr("lib_ui_view_clocksetv_subColor").isPresent() ? attrs.getAttr(
        "lib_ui_view_clocksetv_subColor").get().getColorValue() : new Color(0xFF707070);
    mColorFocusMain = attrs.getAttr("lib_ui_view_clocksetv_mainFoucusColor").isPresent() ?
        attrs.getAttr("lib_ui_view_clocksetv_mainFoucusColor").get().getColorValue() :
        new Color(0xFF000000);
    mColorFocusSub = attrs.getAttr("lib_ui_view_clocksetv_subFoucusColor").isPresent() ?
        attrs.getAttr("lib_ui_view_clocksetv_subFoucusColor").get().getColorValue() :
        new Color(0xFFDFB5B8);
    mColorIndicator = attrs.getAttr("lib_ui_view_clocksetv_indicatorColor").isPresent() ?
        attrs.getAttr("lib_ui_view_clocksetv_indicatorColor").get().getColorValue() :
        new Color(0xFFDC4339);
    mTextMainSize = attrs.getAttr("lib_ui_view_clocksetv_mainTextSize").isPresent() ?
        attrs.getAttr("lib_ui_view_clocksetv_mainTextSize").get().getDimensionValue() :
        AttrHelper.fp2px(16, context);
    mTextSubSize = attrs.getAttr("lib_ui_view_clocksetv_subTextSize").isPresent() ?
        attrs.getAttr("lib_ui_view_clocksetv_subTextSize").get().getDimensionValue() :
        AttrHelper.fp2px(12, context);
    mRadiusBig = attrs.getAttr("lib_ui_view_clocksetv_radiusBig").isPresent() ? attrs.getAttr(
        "lib_ui_view_clocksetv_radiusBig").get().getDimensionValue() : AttrHelper.fp2px(18,
        context);
    mRadiusSmall = attrs.getAttr("lib_ui_view_clocksetv_radiusSmall").isPresent() ?
        attrs.getAttr("lib_ui_view_clocksetv_radiusSmall").get().getDimensionValue() :
        AttrHelper.vp2px(3, context);
    mIndicatorWidth = attrs.getAttr("lib_ui_view_clocksetv_indicatorWidth").isPresent() ?
        attrs.getAttr("lib_ui_view_clocksetv_indicatorWidth").get().getDimensionValue() :
        AttrHelper.vp2px(2, context);
    mPadding = attrs.getAttr("lib_ui_view_clocksetv_padding").isPresent() ? attrs.getAttr(
        "lib_ui_view_clocksetv_padding").get().getDimensionValue() : AttrHelper.vp2px(3, context);
  }

  /**
   * 设置背景
   */
  public void setmColorBg(Color colorBg) {
    mColorBg = colorBg;
  }

  /**
   * 设置外圆字颜色
   */
  public void setmColorMain(Color color) {
    mColorMain = color;
  }

  /**
   * 设置外圆字选中颜色
   */
  public void setmColorFocusMain(Color color) {
    mColorFocusMain = color;
  }

 /**
   * 设置内圆字颜色
   */
  public void setmColorSub(Color color) {
    mColorSub = color;
  }

 /**
   * 设置内圆字选中颜色
   */
  public void setmColorFocusSub(Color color) {
    mColorFocusSub = color;
  }

/**
   * 设置指示器颜色
   */
  public void setmColorIndicator(Color color) {
    mColorIndicator = color;
  }

/**
   * 设置外圆字体
   */
  public void setmTextMainSize(int size) {
    mTextMainSize = size;
  }

/**
   * 设置指示器大小
   */
  public void setmRadiusBig(int size) {
    mRadiusBig = size;
  }

/**
   * 设置中心圆大小
   */
  public void setmRadiusSmall(int size) {
    mRadiusSmall = size;
  }

/**
   * 设置内圆字体
   */
  public void setmTextSubSize(int size) {
    mTextSubSize = size;
  }

  /**
   * 设置指示器宽度
   */
  public void setmIndicatorWidth(int size) {
    mIndicatorWidth = size;
  }

  /**
   * 设置填充距离
   */
  public void setmPadding(int size) {
    mPadding = size;
  }

  private void init(Context context) {
    mPaint = new Paint();
    mPaint.setColor(mColorMain);
    mPaint.setStyle(Paint.Style.FILL_STYLE);
    mPaint.setTextSize(mTextSubSize);
    mPaint.setTextAlign(TextAlignment.CENTER);
    mPaint.setDither(true);
    mPaint.setFilterBitmap(true);
    mTextHeightSub = getTextHeight(mPaint);
    mPaint.setTextSize(mTextMainSize);
    mTextHeightMain = getTextHeight(mPaint);
    mTextHeight = mTextHeightMain;

    setTouchEventListener(this::onTouchEvent);
    setEstimateSizeListener(this::onEstimateSize);
    addDrawTask(this::onDraw);
  }

  @Override
  public void onDraw(Component component, Canvas canvas) {
    mPaint.setColor(mColorBg);
    canvas.drawCircle(mWidth / 2f, mHeight / 2f, Math.min(mWidth, mHeight) / 2f, mPaint);
    if (mMode == 0) {
      mPaint.setColor(mColorSub);
      mPaint.setTextSize(mTextSubSize);
      mTextHeight = mTextHeightSub;
      drawDial(canvas, Math.min(mWidth, mHeight) / 2f - mRadiusBig * 3f - mPadding, mShift,
          HOURS_PM);
      mPaint.setColor(mColorMain);
      mPaint.setTextSize(mTextMainSize);
      mTextHeight = mTextHeightMain;
      drawDial(canvas, Math.min(mWidth, mHeight) / 2f - mRadiusBig - mPadding, !mShift, HOURS_AM);
    } else {
      mPaint.setColor(mColorMain);
      mPaint.setTextSize(mTextMainSize);
      mTextHeight = mTextHeightMain;
      drawDial(canvas, Math.min(mWidth, mHeight) / 2f - mRadiusBig - mPadding, true, MINUTE);
    }
  }

  private void drawDial(Canvas canvas, float radius, boolean withC, String[] dials) {
    canvas.save();
    canvas.translate(mWidth / 2f, mHeight / 2f);
    // mPaint.setXfermode(null);
    for (int i = 0; i < dials.length; i++) {
      final float x = (float) (radius * Math.sin(Math.PI / 6 * i));
      final float y = (float) (-radius * Math.cos(Math.PI / 6 * i)) + mTextHeight / 2f;
      canvas.drawText(mPaint, dials[i], x, y);
    }

    if (withC) {
      // Draw indicator
      canvas.rotate(mSweepAngle);
      mPaint.setColor(mColorIndicator);
      mPaint.setStrokeWidth(6);
      canvas.drawLine(0, 0, 0, -radius, mPaint);
      // Draw small circle
      canvas.drawCircle(0, 0, mRadiusSmall, mPaint);
      canvas.restore();

      // Draw big circle
      // final int sc = canvas.saveLayer(0, 0, mWidth, mHeight, mPaint, Canvas.ALL_SAVE_FLAG);
      final int sc = canvas.saveLayer(new RectFloat(0, 0, mWidth, mHeight), mPaint);
      canvas.translate(mWidth / 2f, mHeight / 2f);
      canvas.rotate(mSweepAngle);
      canvas.drawCircle(0, -radius, mRadiusBig, mPaint);

      canvas.rotate(-mSweepAngle);
      // Draw shadow
      // mPaint.setXfermode(mXfermodes[1]);
      mPaint.setColor(mMode == 0 && mShift ? mColorFocusSub : mColorFocusMain);
      for (int i = 0; i < dials.length; i++) {
        final float x = (float) (radius * Math.sin(Math.PI / 6 * i));
        final float y = (float) (-radius * Math.cos(Math.PI / 6 * i)) + mTextHeight / 2f;
        canvas.drawText(mPaint, dials[i], x, y);
      }

      if (mSweepAngle % 30 != 0) {
        // Draw big center circle
        canvas.rotate(mSweepAngle);
        canvas.drawCircle(0, -radius, mRadiusSmall * 0.618f, mPaint);
      }
    }
    canvas.restore();
  }

  @Override
  public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
    mWidth = EstimateSpec.getSize(widthMeasureSpec);
    mHeight = EstimateSpec.getSize(heightMeasureSpec);
    setEstimatedSize(mWidth, mHeight);
    return false;
  }

  @Override
  public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
    final float eX = touchEvent.getPointerPosition(0).getX();
    final float eY = touchEvent.getPointerPosition(0).getY();
    final float cX = eX - mWidth / 2f;
    final float cY = -eY + mHeight / 2f;
    switch (touchEvent.getAction()) {
      case TouchEvent.PRIMARY_POINT_DOWN:
      case TouchEvent.POINT_MOVE:
        final boolean oldShift = mShift;
        final int oldSweepAngle = mSweepAngle;
        mSweepAngle = (int) Math.toDegrees(Math.atan(cX / cY));
        if (cX == 0 && cY > 0) {
          mSweepAngle = 0;
        } else if (cX == 0 && cY < 0) {
          mSweepAngle = 180;
        } else if (cX > 0 && cY == 0) {
          mSweepAngle = 90;
        } else if (cX < 0 && cY == 0) {
          mSweepAngle = 270;
        } else if (cX > 0 && cY < 0) {
          mSweepAngle += 180;
        } else if (cX < 0 && cY < 0) {
          mSweepAngle += 180;
        } else if (cX < 0 && cY > 0) {
          mSweepAngle += 360;
        }
        mSweepAngle = Math.max(0, mSweepAngle);
        mSweepAngle = Math.min(360, mSweepAngle);
        final int piece = mMode == MODE_MINUTE ? 6 : 30;
        mSweepAngle = (mSweepAngle + piece / 2) / piece * piece;
        if (mSweepAngle >= 360) {
          mSweepAngle = 0;
        }

        float r = (float) Math.sqrt(cX * cX + cY * cY);
        mShift = r <= Math.min(mWidth, mHeight) / 2f - mRadiusBig * 2f;
        if (mShift != oldShift || mSweepAngle != oldSweepAngle) {
          if (mListener != null) {
            int index = ExChange.angle2Index(mMode, mShift, mSweepAngle);
            mListener.onSelect(mMode, ExChange.index2Value(mMode, index));
          }
          invalidate();
        }
        return true;
      case TouchEvent.PRIMARY_POINT_UP:
        return true;
    }
    return true;
  }

  // index 取 0-59 的整数
  public void setMode(int mode, int index) {
    this.mMode = mode;
    if (mode == MODE_HOUR) {
      index = Math.max(0, index);
      index = Math.min(23, index);
      this.mShift = index == 0 || index > 12;
    }
    this.mSweepAngle = ExChange.index2Angle(mode, index);
    this.invalidate();
  }

  public void setOnSelectListener(OnSelectListener l) {
    this.mListener = l;
  }

  public interface OnSelectListener {
    void onSelect(int mode, int value);
  }

  static class ExChange {

    static int index2Angle(int mode, int index) {
      final int piece = mode == MODE_MINUTE ? 6 : 30;
      return index * piece % 360;
    }

    static int angle2Index(int mode, boolean shift, int angle) {
      final int piece = mode == MODE_MINUTE ? 6 : 30;
      int index = angle / piece;
      if (mode == MODE_HOUR) {
        if (shift) {
          if (index == 0) {
            index = 0;
          } else {
            index += 12;
          }
        } else {
          if (index == 0) {
            index = 12;
          }
        }
      }
      return index;
    }

    static int value2Index(int mode, int value) {
      if (mode == MODE_HOUR) {
        return value;
      } else if (mode == MODE_MINUTE) {
        return value;
      }
      return value;
    }

    static int index2Value(int mode, int index) {
      return index;
    }
  }

  public float getTextHeight(Paint p) {
    Paint.FontMetrics fm = p.getFontMetrics();
    return (float) ((Math.ceil(fm.descent - fm.top) + 2.0D) / 2.0D);
  }
}

